/**
 *
 */
package commons.beancopier.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.ClassUtils;

import com.tmall.common.beancopier.AbstractPropertyCopier;
import com.tmall.common.beancopier.BeanCopier;
import com.tmall.common.beancopier.mapper.BeanMapperUtils;
import com.tmall.common.beancopier.mapper.PropertiesMapper;
import com.tmall.common.beancopier.mapper.PropertiesMapperBuilder;

/**
 * @author 陌铭(Phenix Huang)
 *
 */
public class CollectionPropertyCopier extends AbstractPropertyCopier {



    public CollectionPropertyCopier() {
        super();
        // TODO Auto-generated constructor stub
    }

    public CollectionPropertyCopier(PropertiesMapperBuilder mapper, BeanCopier copier) {
        super(mapper, copier);
        // TODO Auto-generated constructor stub
    }

    /*
     *
     * @see net.phenix.comp.util.beancopier.AbstractPropertyCopier#smartCopyProperty(java.lang.Object, java.lang.String, java.lang.Class, java.lang.Object)
     */
    public void smartCopyProperty(Object dest, String propertyName, Class<?> propertyType, Object value) throws Exception {
        Collection<?> source = (Collection<?>)value ;
        Collection<Object> colDest = null ;
        if (PropertyUtils.isReadable(dest, propertyName)) {
            colDest = (Collection<Object>)PropertyUtils.getProperty(dest, propertyName);
        }
        if (colDest == null || colDest == Collections.EMPTY_LIST || colDest == Collections.EMPTY_SET) {
            if (!PropertyUtils.isWriteable(dest, propertyName)) {
                return ;
            }
            colDest = (Collection<Object>)tryToNewInstance(value.getClass());
            if (colDest == null) {
                if (ClassUtils.isAssignable(propertyType, List.class)) {
                    colDest = new ArrayList<Object>(source.size());
                } else if (ClassUtils.isAssignable(propertyType, Set.class)) {
                    colDest = new HashSet<Object>();
                } else {//try to create a new object base on the source object.
                    throw new IllegalArgumentException("can not clone such a Collection instance: "+propertyType);
                }
            }
            PropertyUtils.setProperty(dest, propertyName, colDest);
        }
        Map<String, String> propertyMap = Collections.emptyMap();
        for (Object subSource : source) {
            if (subSource == null) {
                continue ;
            }
            if (propertyMap == Collections.EMPTY_MAP) {
                PropertiesMapper mapper = getPropertyMapper(subSource.getClass());
                if (mapper == null) {
                    return ;
                }
                propertyMap = BeanMapperUtils.getPropertyNameMap(subSource.getClass());
                BeanMapperUtils.filterProperties(propertyMap, mapper);
            }
            Object subDest = cloneAndFilterObject(dest, subSource, subSource.getClass(), propertyMap);
            colDest.add(subDest);
        }
        return ;
    }

    /*
     * @see net.phenix.comp.util.beancopier.AbstractPropertyCopier#couldSmartCopy(java.lang.Class)
     */
    @Override
    public boolean couldSmartCopy(Class<?> propertyType) {
        return ClassUtils.isAssignable(propertyType, Collection.class);
    }

}
